import cv2
import func_timeout
from multiprocessing import Queue

from process import Process
from utils import OrderQueue
from logger import logger


class StreamPuller(Process):

    def __init__(self, config, error_queue, aux_queue=False):
        super().__init__()
        self._stream_queue = Queue()
        self._aux_stream_queue = None
        if aux_queue:
            self._aux_stream_queue = Queue()
            self._add_process(
                config, 
                error_queue,
                self._stream_queue, 
                self._aux_stream_queue
            )
        else:
            self._add_process(
                config, 
                error_queue,
                self._stream_queue
            )
        self._start()
        try:
            config.stream_w, config.stream_h, config.stream_fps = self._stream_queue.get(timeout=5)
        except:
            raise RuntimeError('视频流连接失败...')
    
    def _run(self, config, error_queue, stream_queue, aux_stream_queue=None):
        input = config.input
        cap = cv2.VideoCapture(int(input) if input.isdigit() else input)
        if not cap.isOpened():
            error_queue.put(f'无法读取视频流[{input}]，请检查能否成功连通设备，以及设备的视频流能否正常播放')
            return
        fps = cap.get(cv2.CAP_PROP_FPS)
        if fps > 120:
            error_queue.put(f'获取帧率过高：{fps}，判断拉流出现了问题，无法正常拉流')
            return
        resize = tuple(map(int, config.resolution.split('*'))) if config.resolution else None
        
        if resize:
            logger.info(f'分辨率将被转为{resize[0]}*{resize[1]}，帧率为{fps}')
            stream_queue.put((resize[0], resize[1], fps))
        else:
            w, h = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            logger.info(f'获取原始视频流分辨率为{w}*{h}，帧率为{fps}')
            stream_queue.put((w, h, fps))
            del w, h

        queue_size_threshhold = config.mutiprocess_worker_num if config.multiprocess else 1

        frame_id = 0  # 给每个帧弄一个递增的编号

        self._is_quit = False

        del fps, config
        
        # 加个超时包装
        @func_timeout.func_set_timeout(5)
        def read():
            return cap.read()

        while True:
            try:
                ret, frame = read()  # 这个函数如果卡太久说明有问题，直接重连试试
            except func_timeout.FunctionTimedOut:
                ret, frame = False, None
                logger.info('读取视频流超时（5s内无法读取一帧）')
            if not ret:  # frame可能为None，这里加个判断
                logger.info('读取视频流出现问题，即将尝试重新读取视频流...')
                cap = cv2.VideoCapture(input)
                if not cap.isOpened() or cap.get(cv2.CAP_PROP_FPS) > 120:
                    error_queue.put('拉流出现问题，无法正常获取设备rtsp视频流')
                    return  # 拉流还有问题就不重试了，直接退出，因为这个问题大概率不可能在几秒内解决，循环重试还浪费性能，就直接让nvr-manager过一段时间再重启程序吧
                else:
                    logger.info('重新读取视频流成功')
                continue
            if frame is None:
                continue
            if resize:
                frame = cv2.resize(frame, resize)
            if stream_queue.qsize() < queue_size_threshhold:  # 加个阈值，避免数据处理不过来在队列里堆积的，导致延迟越来越高
                stream_queue.put((frame_id, frame))
                frame_id = (frame_id + 1) % OrderQueue.ID_LIMIT
            if aux_stream_queue and aux_stream_queue.qsize() < 1:
                aux_stream_queue.put(frame)

    @property
    def stream_queue(self):
        return self._stream_queue

    @property
    def aux_stream_queue(self):
        return self._aux_stream_queue

    @property
    def w(self):
        return self._w
    
    @property
    def h(self):
        return self._h
    
    @property
    def fps(self):
        return self._fps
